
#include "mp4Parse.h"

/**
字色              背景              颜色
---------------------------------------
30                40              黑色
31                41              紅色
32                42              綠色
33                43              黃色
34                44              藍色
35                45              紫紅色
36                46              青藍色
37                47              白色
*/

// 颜色定义
typedef enum
{
    BLACK = 30,
    RED = 31,
    GREEN = 32,
    YELLOW = 33,
    BLUE = 34,
    PURPLE = 35,
    CYAN = 36,
    WHITE = 37

} colors_e;

static char *logColor = "\033[%dm";

/**
 * 调整控制台字色
 */
uint32_t setLogColor(colors_e color)
{
    printf(logColor, color);
    return 0;
}

// 大小端转换函数
uint64_t endianConvert(uint8_t *data, uint32_t datalen)
{
    uint64_t res = 0;
    uint8_t *p = NULL;

    p = (uint8_t *)&res;

    for (int i = 0; i < datalen; i++)
    {
        // printf("%#x ", data[i]);
        // printf("%d  %d\n", i, datalen - i - 1);
        p[i] = data[datalen - i - 1];
    }
    // printf("--> %d \n", res);
    // printf(" \n");
    return res;
}

uint32_t FTYP(uint8_t *data, uint32_t len)
{
    mp4ftypBox_t mp4ftypBox = {0};
    memcpy(&mp4ftypBox, data, len);
    printf("FTYP majorBrand [%s] minorVersion [%d] compatibleBrands [%s]\n",
           mp4ftypBox.majorBrand,
           endianConvert((uint8_t *)&mp4ftypBox.minorVersion, 4),
           mp4ftypBox.compatibleBrands);
    return 0;
}

uint32_t MOOV(uint8_t *data, uint32_t len)
{
    mp4ParseMoovBox_t mp4ParseMoovBox = {0};
    memcpy(&mp4ParseMoovBox, data, sizeof(mp4ParseMoovBox_t));
    setLogColor(CYAN);

    printf("MOOV size [%d] type [%s] version [%d] ",
           endianConvert((uint8_t *)&mp4ParseMoovBox.basicBox.size, 4),
           mp4ParseMoovBox.basicBox.type,
           mp4ParseMoovBox.version);

    uint64_t creationTime = endianConvert((uint8_t *)&mp4ParseMoovBox.creationTime, 4);
    printf("creationTime %d ", creationTime);
    uint64_t modificationTime = endianConvert((uint8_t *)&mp4ParseMoovBox.modificationTime, 4);
    printf("modificationTime %d ", modificationTime);
    uint64_t palyTime = endianConvert((uint8_t *)&mp4ParseMoovBox.duration, 4) / endianConvert((uint8_t *)&mp4ParseMoovBox.timeScale, 4);
    printf("palyTime %ds ", palyTime);

    uint32_t rate,
        volume;

    rate = mp4ParseMoovBox.rate[0];
    rate <<= 8;
    rate |= mp4ParseMoovBox.rate[1];
    printf("rate [%d.", rate);
    rate = mp4ParseMoovBox.rate[2];
    rate <<= 8;
    rate |= mp4ParseMoovBox.rate[3];
    printf("%d] ", rate);

    volume = mp4ParseMoovBox.volume[0];
    printf("volume[%d.", volume);
    volume = mp4ParseMoovBox.volume[1];
    rate <<= 8;
    volume |= mp4ParseMoovBox.volume[2];
    rate <<= 8;
    volume |= mp4ParseMoovBox.volume[3];
    printf("%d]\n", volume);
    setLogColor(BLACK);

    uint32_t childDataLen = len - sizeof(mp4ParseMoovBox_t);
    printf("MOOV childDataLen %d\n", childDataLen);

    childBoxParse(data + sizeof(mp4ParseMoovBox_t), childDataLen);
    //
    return 0;
}

uint32_t UUID(uint8_t *data, uint32_t len)
{
    printf("UUID box\n");
    // childBoxParse(data, len);
    return 0;
}

uint32_t MDAT(uint8_t *data, uint32_t len)
{
    printf("MDAT box\n");
    // childBoxParse(data, len);
    return 0;
}

uint32_t TRAK(uint8_t *data, uint32_t len)
{
    mp4ParseTkhdBox_t mp4ParseTkhdBox = {0};
    printf("TRAK mp4ParseTkhdBox_t size %d \n", sizeof(mp4ParseTkhdBox_t));

    setLogColor(CYAN);
    // tkhd
    memcpy(&mp4ParseTkhdBox, data, sizeof(mp4ParseTkhdBox_t));
    printf("TRAK size [%d] type [%s] version [%d] layer [%d] group [%d] width [%d] height [%d] ",
           endianConvert((uint8_t *)&mp4ParseTkhdBox.basicBox.size, 4),
           mp4ParseTkhdBox.basicBox.type,
           mp4ParseTkhdBox.version,
           endianConvert((uint8_t *)&mp4ParseTkhdBox.layer, 2),
           endianConvert((uint8_t *)&mp4ParseTkhdBox.group, 2),
           endianConvert((uint8_t *)&mp4ParseTkhdBox.width, 2),
           endianConvert((uint8_t *)&mp4ParseTkhdBox.height, 2)

    );

    uint64_t creationTime = endianConvert((uint8_t *)&mp4ParseTkhdBox.creationTime, 4);
    printf("creationTime %d ", creationTime);
    uint64_t modificationTime = endianConvert((uint8_t *)&mp4ParseTkhdBox.modificationTime, 4);
    printf("modificationTime %d ", modificationTime);
    uint64_t duration = endianConvert((uint8_t *)&mp4ParseTkhdBox.duration, 4);
    printf("duration %d ", duration);

    uint8_t *p = (uint8_t *)&mp4ParseTkhdBox.volume;
    uint32_t volume = p[0];
    printf("volume[%d.", volume);
    volume = p[1];
    printf("%d]\n", volume);
    setLogColor(BLACK);

    //
    uint32_t childDataLen = len - sizeof(mp4ParseTkhdBox_t);
    printf("TRAK childDataLen %d\n", childDataLen);
    childBoxParse(data + sizeof(mp4ParseTkhdBox_t), childDataLen);
    return 0;
}

uint32_t TKHD(uint8_t *data, uint32_t len)
{
    mp4ParseTkhdBox_t mp4ParseTkhdBox = {0};
    printf("TKHD mp4ParseTkhdBox_t size %d \n", sizeof(mp4ParseTkhdBox_t));

    //
    uint32_t childDataLen = len - sizeof(mp4ParseTkhdBox_t);
    printf("TKHD childDataLen %d\n", childDataLen);
    childBoxParse(data + sizeof(mp4ParseTkhdBox_t), childDataLen);
    return 0;
}

uint32_t EDTS(uint8_t *data, uint32_t len)
{
    printf("EDTS \n");

    return 0;
}

uint32_t MDIA(uint8_t *data, uint32_t len)
{
    mp4ParseMdhdBox_t mp4ParseMdhdBox = {0};
    printf("MDIA mp4ParseMdhdBox_t size %d \n", sizeof(mp4ParseMdhdBox_t));
    setLogColor(CYAN);
    memcpy(&mp4ParseMdhdBox, data, sizeof(mp4ParseMdhdBox_t));
    printf("MDIA size [%d] type [%s] version [%d] timeScale [%d]  language [%c%c] ",
           endianConvert((uint8_t *)&mp4ParseMdhdBox.basicBox.size, 4),
           mp4ParseMdhdBox.basicBox.type,
           mp4ParseMdhdBox.version,
           endianConvert((uint8_t *)&mp4ParseMdhdBox.timeScale, 4),
           mp4ParseMdhdBox.language[0],
           mp4ParseMdhdBox.language[1]

    );

    uint64_t creationTime = endianConvert((uint8_t *)&mp4ParseMdhdBox.creationTime, 4);
    printf("creationTime %d ", creationTime);
    uint64_t modificationTime = endianConvert((uint8_t *)&mp4ParseMdhdBox.modificationTime, 4);
    printf("modificationTime %d ", modificationTime);
    uint64_t duration = endianConvert((uint8_t *)&mp4ParseMdhdBox.duration, 4);
    printf("duration %d \n", duration);
    setLogColor(BLACK);

    //
    uint32_t childDataLen = len - sizeof(mp4ParseMdhdBox_t);
    printf("MDIA childDataLen %d\n", childDataLen);
    childBoxParse(data + sizeof(mp4ParseMdhdBox_t), childDataLen);
    return 0;
}

uint32_t HDLR(uint8_t *data, uint32_t len)
{

    mp4ParseHdlrBox_t mp4ParseHdlrBox = {0};
    uint32_t structSize = sizeof(mp4ParseHdlrBox_t);
    printf("HDLR mp4ParseHdlrBox_t size %d \n", structSize);

    memcpy(&mp4ParseHdlrBox, data, structSize);
    printf("HDLR version [%d] \n",
           //    endianConvert((uint8_t *)&mp4ParseHdlrBox.basicBox.size, 4),
           //    mp4ParseHdlrBox.basicBox.type,
           mp4ParseHdlrBox.version

    );

    // 数据长度减去结构体(不包含基础头,名称字段)数据长度,剩下的就是名字
    uint32_t nameLen = len - sizeof(mp4ParseHdlrBox_t);
    printf("HDLR nameLen %d \n", nameLen);
    uint8_t *name = (uint8_t *)malloc(nameLen);
    if (name == NULL)
    {
        printf("HDLR malloc fail\n");
        return -1;
    }

    memset(name, 0, nameLen);
    memcpy(name, data + structSize, nameLen);

    printf("HDLR name [%s]\n", name);

    if (name != NULL)
    {
        free(name);
        name = NULL;
    }

    return 0;
}

uint32_t MDHD(uint8_t *data, uint32_t len)
{
    printf("MDHD \n");

    return 0;
}

uint32_t MINF(uint8_t *data, uint32_t len)
{
    mp4ParseVmhdBox_t mp4ParseVmhdBox = {0};
    uint32_t structSize = sizeof(mp4ParseVmhdBox_t);
    printf("MINF mp4ParseVmhdBox_t %d \n", structSize);

    childBoxParse(data, len);
    return 0;
}

uint32_t VMHD(uint8_t *data, uint32_t len)
{
    mp4ParseVmhdBox_t mp4ParseVmhdBox = {0};
    uint32_t structSize = sizeof(mp4ParseVmhdBox_t);
    printf("VMHD mp4ParseVmhdBox_t %d \n", structSize);

    memcpy(&mp4ParseVmhdBox, data, structSize);
    printf("VMHD version [%d] graphicsMode [%d] opcolor [",
           mp4ParseVmhdBox.version,
           endianConvert((uint8_t *)&mp4ParseVmhdBox.graphicsMode, 4));

    for (int i = 0; i < 6; i++)
    {
        printf("%#x ", mp4ParseVmhdBox.opcolor[i]);
    }
    printf("]\n");
    return 0;
}

uint32_t DINF(uint8_t *data, uint32_t len)
{
    mp4ParseVmhdBox_t mp4ParseVmhdBox = {0};
    uint32_t structSize = sizeof(mp4ParseVmhdBox_t);
    printf("DINF mp4ParseVmhdBox_t %d \n", structSize);
    memcpy(&mp4ParseVmhdBox, data, structSize);

    childBoxParse(data, len);
    return 0;
}

uint32_t DREF(uint8_t *data, uint32_t len)
{
    mp4ParseDrefBox_t mp4ParseDrefBox = {0};
    uint32_t structSize = sizeof(mp4ParseDrefBox_t);
    printf("DREF mp4ParseDrefBox_t %d \n", structSize);

    memcpy(&mp4ParseDrefBox, data, structSize);
    printf("DREF version [%d] count [%d]  \n",
           mp4ParseDrefBox.version,
           endianConvert((uint8_t *)&mp4ParseDrefBox.count, 4));

    // 数据长度减去结构体(不包含基础头,名称字段)数据长度,剩下的就是名字
    uint32_t urlLen = len - sizeof(mp4ParseDrefBox_t);
    printf("DREF urlLen %d \n", urlLen);
    uint8_t *name = (uint8_t *)malloc(urlLen);
    if (name == NULL)
    {
        printf("DREF malloc fail\n");
        return -1;
    }

    memset(name, 0, urlLen);
    memcpy(name, data + structSize, urlLen);

    for (int i = 0; i < urlLen; i++)
    {
        printf("%c", name[i]);
    }
    printf("\n");

    if (name != NULL)
    {
        free(name);
        name = NULL;
    }

    return 0;
}

uint32_t STBL(uint8_t *data, uint32_t len)
{
    printf("STSZ  \n");
    childBoxParse(data, len);
    return 0;
}

uint32_t STSD(uint8_t *data, uint32_t len)
{
    printf("STSD  \n");
    return 0;
}

uint32_t STTS(uint8_t *data, uint32_t len)
{
    printf("STTS  \n");
    return 0;
}

uint32_t STSS(uint8_t *data, uint32_t len)
{
    printf("STSS  \n");
    return 0;
}

uint32_t SDTP(uint8_t *data, uint32_t len)
{
    printf("SDTP  \n");
    return 0;
}
uint32_t STSC(uint8_t *data, uint32_t len)
{
    printf("STSC  \n");
    return 0;
}
uint32_t STSZ(uint8_t *data, uint32_t len)
{
    printf("STSZ  \n");
    return 0;
}
uint32_t STCO(uint8_t *data, uint32_t len)
{
    printf("STCO  \n");
    return 0;
}
uint32_t CTTS(uint8_t *data, uint32_t len)
{
    printf("CTTS  \n");
    return 0;
}
uint32_t UDTA(uint8_t *data, uint32_t len)
{
    printf("UDTA  \n");
    return 0;
}

mp4ParseBoxFunc_t boxFunc[] = {
    {"ftyp", FTYP}, // 文件类型

    //
    {"moov", MOOV}, // 描述mp4文件媒体数据

    //
    {"uuid", UUID}, // 用户自定义box

    //
    {"mdat", MDAT}, // 媒体数据内容

    // 子
    {"trak", TRAK},
    {"tkhd", TKHD},
    {"edts", EDTS},
    {"mdia", MDIA},
    {"hdlr", HDLR},
    {"mdhd", MDHD},
    {"minf", MINF},
    {"vmhd", VMHD},
    {"dinf", DINF},
    {"dref", DREF},
    {"stbl", STBL},
    {"stsd", STSD},
    {"stts", STTS},
    {"stss", STSS},
    {"sdtp", SDTP},
    {"stsc", STSC},
    {"stsz", STSZ},
    {"stco", STCO},
    {"ctts", CTTS},
    {"udta", UDTA},

};

uint32_t childBoxParse(uint8_t *rawData, uint32_t datalen)
{
    uint32_t index = 0;
    bool isRun = false;
    do
    {

        mp4BasicBox_t mp4BasicBox = {0};

        //
        isRun = false;

        //
        memcpy(&mp4BasicBox, rawData + index, sizeof(mp4BasicBox_t));
        index += sizeof(mp4BasicBox_t);

        //
        uint64_t size = endianConvert((void *)&mp4BasicBox.size, 4);
        if (size == 0)
        {
            break;
        }

        printf("CHILD size [%d] type [%s] datalen [%d]\n", size, mp4BasicBox.type, datalen);
        if (size == 1)
        {
            printf("CHILD specical size %d!!!!!\n", size);

            // 此时的大小是超越普通box的范围的,应该继续向后读取更大的结构
            mp4FullBox_t mp4FullBox = {0};
            printf("CHILD specical %d\n", sizeof(mp4FullBox_t));
            memcpy(&mp4FullBox, rawData + index, sizeof(mp4FullBox_t));
            index += sizeof(mp4FullBox_t);
            size = endianConvert(mp4FullBox.largeSize, 8);
            printf("CHILD specical largeSize [%d] \n", size);
        }

        // 分类计算各个box
        uint8_t boxFuncCount = sizeof(boxFunc) / sizeof(boxFunc[0]);
        for (int i = 0; i < boxFuncCount; i++)
        {
            if (memcmp(mp4BasicBox.type, boxFunc[i].type, 4) == 0)
            {

                uint32_t len = 0;
                uint8_t *data = NULL;

                // 继续
                isRun = true;

                len = size - sizeof(mp4BasicBox_t);

                //
                data = (uint8_t *)malloc(len);
                if (data == NULL)
                {
                    printf("CHILD malloc fail size [%d]\n", len);
                    break;
                }

                // 读取数据
                printf("CHILD data len [%d]\n", len);
                memcpy(data, rawData + index, len);

                // 处理
                boxFunc[i].cb(data, len);

                // 向后
                index += len;
                if (data != NULL)
                {
                    free(data);
                    data = NULL;
                }
                break;
            }
        }

    } while (isRun);

    return 0;
}

//
int8_t mp4Parse(uint8_t *fileAddr)
{
    FILE *file = NULL;
    file = fopen(fileAddr, "rb");
    if (file == NULL)
    {
        printf("MAIN open file fail\n");
        return -1;
    }

    fseek(file, 0, SEEK_SET);

    bool isRun = false;
    do
    {

        mp4BasicBox_t mp4BasicBox = {0};

        //
        isRun = false;

        //
        // printf("size %d\n", sizeof(mp4BasicBox_t));
        printf("MAIN ---> next box start addr %#x\n", ftell(file));
        int32_t readLen = fread(&mp4BasicBox, 1, sizeof(mp4BasicBox_t), file);
        if (readLen <= 0)
        {
            printf("fread fail !!!\n");
            break;
        }
        uint64_t size = endianConvert((void *)&mp4BasicBox.size, 4);
        if (size == 0)
        {
            break;
        }
        printf("MAIN size [%d] %#x type [%s]\n", size, size, mp4BasicBox.type);
        if (size == 1)
        {

            printf("MAIN specical size %d!!!!!\n", size);

            // 此时的大小是超越普通box的范围的,应该继续向后读取更大的结构
            // 此时的大小是超越普通box的范围的,应该继续向后读取更大的结构
            mp4FullBox_t mp4FullBox = {0};
            printf("MAIN specical %d\n", sizeof(mp4FullBox_t));
            fread(&mp4FullBox, 1, sizeof(mp4FullBox_t), file);
            size = endianConvert(mp4FullBox.largeSize, 8);
            printf("MAIN specical largeSize [%d] \n", size);
        }

        // 分类计算各个box
        uint8_t boxFuncCount = sizeof(boxFunc) / sizeof(boxFunc[0]);
        for (int i = 0; i < boxFuncCount; i++)
        {
            if (memcmp(mp4BasicBox.type, boxFunc[i].type, 4) == 0)
            {

                uint32_t len = 0;
                uint8_t *data = NULL;

                // 继续
                isRun = true;

                len = size - sizeof(mp4BasicBox_t);
                data = (uint8_t *)malloc(len);
                if (data == NULL)
                {
                    printf("MAIN malloc fail size [%d]\n", len);
                    break;
                }
                printf("MAIN data start addr %#x  data len [%d]\n", ftell(file), len);

                // 读取数据
                fread(data, 1, len, file);

                boxFunc[i].cb(data, len);

                if (data != NULL)
                {
                    free(data);
                    data = NULL;
                }
                break;
            }
        }

    } while (isRun);

    printf("parse ok!\n");
    fclose(file);
    return 0;
}

int main(int argc, char *argv[])
{
    printf("param count %d\n", argc);
    for (int i = 0; i < argc; i++)
    {
        printf("param[%d] %s\n", i, argv[i]);
    }

    if (argc < 2)
    {
        printf("This tool is only used to parse mp4 files, and is very basic, for reference only\n");
        printf("Usage is as follows: *.exe *.mp4\n");
        return 0;
    }

    if ((strcmp(argv[1], "help") == 0) || (strcmp(argv[1], "-h") == 0))
    {
        printf("This tool is only used to parse mp4 files, and is very basic, for reference only\n");
        printf("Usage is as follows: *.exe *.mp4\n");
        return 0;
    }

    mp4Parse(argv[1]);
    return 0;
}
